<?php

namespace app\service;

use app\common\controller\AppCommon;

class OnlineService
{
    private $up_sql = null;

    public function __construct()
    {
        if (!AppCommon::query("SHOW TABLES LIKE '" . table_name('app_version') . "';")) {
            AppCommon::execute("CREATE TABLE `" . table_name('app_version') . "` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `version` varchar(20) DEFAULT '' COMMENT '版本字符串，如1.0.0',
  `content` varchar(1000) DEFAULT '' COMMENT '更新说明',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8mb4;");
        }
    }

    //检查更新
    public function check(): array
    {
        $query = AppCommon::data_get('app_version');

        $api = 'https://ser.ok8.top/index/version/check_update';
        $currentVersion = $query['version'] ?? '1.0.0';
        $data = [
            'version' => $currentVersion,
            'app' => SHOP_TYPE,
            'secret' => SHOP_SECRET,
            'ip' => request()->ip(),
            'uniqid' => uniqid('bs'),
            'domain' => request()->host(),
        ];
        $res = json_decode(curl_get_request($api, $data), true);
        if ($res['code'] != 0 || empty($res['data']['need_update'])) {
            return data_return_arr('无需更新', -1, ['version' => $currentVersion]);
        }
        return data_return_arr('success', 0, ['version' => $currentVersion, 'new' => $res['data']['data']]);
    }

    public function update($info): array
    {
        $query = AppCommon::data_get('app_version');
        $currentVersion = $query['version'] ?? '1.0.0';

        $url = $info['url'];
        $localPath = RUNTIME_PATH . '/tmp/update/';
        if (!is_dir($localPath)) {
            mkdir($localPath, 0777, true);
        }
        if (!is_dir($localPath) || !is_writable($localPath)) {
            return data_return_arr(RUNTIME_PATH . '目录需要写权限', -1);
        } else {
            //清空目录
            del_dir($localPath);
        }
        //备份目录
        $backPath = RUNTIME_PATH . 'tmp/backup/' . $currentVersion;
        if (!is_dir($backPath)) {
            mkdir($backPath, 0777, true);
        }
        if (!is_dir($backPath) || !is_writable($backPath)) {
            return data_return_arr(RUNTIME_PATH . '目录需要写权限', -1);
        }

        $tmpFilesPath = RUNTIME_PATH . '/tmp/update/files';
        if (!is_dir($tmpFilesPath)) {
            mkdir($tmpFilesPath, 0777, true);
        }
        $localFile = $localPath . $info['version'] . '.zip';
        //下载文件
        $this->download($url, $localFile);
        if (!file_exists($localFile)) {
            return data_return_arr($localPath . '安装包下载失败', -1);
        }
        if (!extension_loaded('zip')) {
            return data_return_arr('环境缺少zip扩展，请安装php的zip扩展', -1);
        }

        $zip = new \ZipArchive;
        if ($zip->open($localFile) !== true) {
            return data_return_arr('无法打开ZIP文件或解压缩失败', -1);
        } else {
            $zip->extractTo($tmpFilesPath);

            $zip->close();
            unlink($localFile);
        }
        $files = $this->list_dir($tmpFilesPath);
        $list = $this->get_file_list($files);

        $this->start_update($list, $backPath, $tmpFilesPath);
        if ($this->up_sql) {
            $prefix = config('database.prefix');
            foreach ($this->up_sql as $item) {
                $item = preg_replace('/\s+/', ' ', $item);
                if (stripos($item, 'DROP TABLE IF EXISTS `')) {
                    $item = str_replace('DROP TABLE IF EXISTS `', 'DROP TABLE IF EXISTS `' . $prefix, $item);
                }
                if (stripos($item, 'CREATE TABLE `')) {
                    $item = str_replace('CREATE TABLE `', 'CREATE TABLE `' . $prefix, $item);
                }
                if (stripos($item, 'INSERT INTO `')) {
                    $item = str_replace('INSERT INTO `', 'INSERT INTO `' . $prefix, $item);
                }
                if (stripos($item, 'ALTER TABLE `') !== false) {
                    $item = str_replace('ALTER TABLE `', 'ALTER TABLE `' . $prefix, $item);
                }
                AppCommon::execute($item);
            }
        }
        del_dir($localPath);
        if (empty($query['id'])) {
            AppCommon::data_add('app_version', ['content' => $info['content'], 'version' => $info['version']]);
        } else {
            AppCommon::data_update('app_version', ['id' => $query['id']], ['content' => $info['content'], 'version' => $info['version']]);
        }


        return data_return_arr('更新完成,备份文件在' . $backPath);
    }

    private function start_update($list, $backupPath, $tmpFilesPath)
    {
        foreach ($list as $file) {
            $fileName = basename($file);
            $dirName = dirname($file);

            //更新数据库
            if ($fileName === 'version_update_sql.php') {
                $this->up_sql = include $tmpFilesPath . $file;
                continue;
            }

            if (file_exists(ROOT_PATH . ltrim($file, '/'))) {
                is_dir($backupPath . '/' . $dirName) || mkdir($backupPath . '/' . $dirName, 0777, true);
                copy(ROOT_PATH . ltrim($file, '/'), $backupPath . '/' . $dirName . '/' . $fileName);
            } else {
                $dirName = dirname(ROOT_PATH . ltrim($file, '/'));
                if (!is_dir($dirName)) {
                    mkdir($dirName, 0777, true);
                }
                file_put_contents(ROOT_PATH . ltrim($file, '/'), file_get_contents($tmpFilesPath . $file));
            }
        }
    }

    private function list_dir($dir)
    {
        if (!is_dir($dir)) {
            return [];
        }
        $files = [];
        $handle = opendir($dir);
        while (($file = readdir($handle)) !== false) {
            if ($file != '.' && $file != '..') {
                $path = $dir . '/' . $file;
                if (is_dir($path)) {
                    $files[$file] = $this->list_dir($path);
                } else {
                    $files[] = $file;
                }
            }
        }
        closedir($handle);
        return $files;
    }

    private function download($remoteFile, $localFile)
    {
        $remoteStream = fopen($remoteFile, 'rb');
        if ($remoteStream === false) {
            return data_return_arr('无法打开远程文件', -1);
        }
        $localStream = fopen($localFile, 'ab');
        if ($localStream === false) {
            return data_return_arr('无法打开本地文件', -1);
        }
        while (!feof($remoteStream)) {
            $buffer = fread($remoteStream, 8192);
            fwrite($localStream, $buffer);
        }
        fclose($remoteStream);
        fclose($localStream);
    }

    private function get_file_list($array, $path = '')
    {
        $result = [];
        foreach ($array as $key => $value) {
            if (is_array($value)) {
                $subPath = $path . '/' . $key;
                $subResult = $this->get_file_list($value, $subPath);
                $result = array_merge($result, $subResult);
            } elseif (is_string($value) && ($this->endsWith($value, ".php") || $this->endsWith($value, ".html"))) {
                $file = $path . '/' . $value;
                $result[] = $file;
            }
        }
        return $result;
    }

    private function endsWith($string, $suffix)
    {
        return substr($string, -strlen($suffix)) === $suffix;
    }
}